﻿#include <QUrl>
#include <QDir>
#include <QFile>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>

#include "qconfiger.h"
#include "qconfiger_p.h"

QConfiger::QConfiger(const QString&  baseDir,QObject *parent)
{
    d_ptr = new QConfigerPrivate(parent);
    d_ptr->q_ptr = this;
    d_ptr->m_baseDir = baseDir;
}

QConfiger::~QConfiger()
{
    delete d_ptr;
}

/*
    返回配置文件存储目录
*/
QString QConfiger::baseDir() const
{
    return d_ptr->m_baseDir;
}

/*
    设置配置文件的存储目录。
*/
void QConfiger::setBaseDir(const QString& dir)
{
    d_ptr->m_baseDir = dir;
}

/*
    将Json对象value中的内容写入filePath所指定的文件中，filePath可以
    是文件绝对路径，也可以是相对baseDir的相对文件路径。

    如果filePath不存在则会新建，如果已存在则会执行字段替换。
    例如当前配置文件内容为:
    {
        "config1": {"p1":"v1"}
        "config2": 1
        "config3": 0
    }

    value中的内容为:
    {
        "config1": {"p2":"v2"}
        "config2": 1
        "config3": 1
    }

    写入后内容为：
    {
        "config1": {"p2":"v2"}
        "config2": 1
        "config3": 0
    }

*/
bool QConfiger::writeConfigFile(const QString&  filePath, QJsonArray value)
{
    QJsonDocument doc(value);
    return d_ptr->writeConfig(filePath,doc);
}

/*
    将Json数组value写入filePath所指定的配置文件中。不同于
    writeConfigFile(QString,QJSonArray)，如果filePath已存在，
    则本函数只会覆盖filePath中的内容，而不是替换。
*/
bool QConfiger::writeConfigFile(const QString&  filePath, QJsonObject value)
{
    QJsonDocument doc(value);
    return d_ptr->writeConfig(filePath,doc);
}

/*
    读取JSON文件filePath中的内容，并返回其值。filePath可以
    是文件绝对路径，也可以是相对baseDir的相对文件路径。
*/
QJsonValue QConfiger::readConfigFile(const QString&  filePath)
{
    return d_ptr->readConfig(filePath);
}

/*
    重命名filePath文件。filePath可以是文件绝对路径，也可以
    是相对baseDir的相对文件路径。
*/
bool QConfiger::renameConfigFile(const QString&  filePath, const QString&  newFileName)
{
    return d_ptr->renameConfig(filePath,newFileName);
}

/*
    删除filePath文件。filePath可以是文件绝对路径，也可以
    是相对baseDir的相对文件路径。
*/
bool QConfiger::removeConfigFile(const QString&  filePath)
{
    return d_ptr->removeConfig(filePath);
}

QConfigerPrivate::QConfigerPrivate(QObject *parent):QObject(parent){

    m_baseDir = "";
}

bool QConfigerPrivate::writeConfig(const QString&  filePath,const QJsonDocument& doc)
{
    QUrl fileUrl(filePath);
    QString fullFilePath;

    if(fileUrl.isRelative()){
        fullFilePath = m_baseDir+"/"+filePath;
    }else{
        fullFilePath = filePath;
    }

    QFile configFile(fullFilePath);
    QFileInfo configFileInfo(configFile);

    if(!configFileInfo.exists())
    {
        QDir dir=configFileInfo.dir();
        if(!dir.exists()){
            if(!dir.mkpath(dir.path())){
                qWarning()<<__FUNCTION__<<": Fail to create dir";
                return false;
            }
        }

        if(!configFile.open(QFile::WriteOnly|QFile::Truncate)){
            qWarning()<<__FUNCTION__<<": Fail to open file in new mode";
            return false;
        }

        QByteArray bytes = doc.toJson();
        configFile.write(bytes);
        configFile.close();

        emit q_ptr->configFileAdded(fullFilePath);
        return true;
    }
    else
    {
        if(!configFile.open(QFile::ReadOnly)){
            qWarning()<<__FUNCTION__<<": Fail to open file in read mode";
            return false;
        }
        QByteArray bytes = configFile.readAll();
        configFile.close();

        QJsonParseError e;
        QJsonDocument oldFileDoc = QJsonDocument::fromJson(bytes,&e);
        if(e.error!=QJsonParseError::NoError)
        {
            if(!configFile.open(QFile::WriteOnly)){
                qWarning()<<__FUNCTION__<<": Fail to open file in content error mode";
                return false;
            }
            QByteArray bytes = doc.toJson();
            configFile.write(bytes);
            configFile.close();

            emit q_ptr->configFileUpdated(fullFilePath);
            return true;
        }
        else
        {
            if(!configFile.open(QFile::WriteOnly)){
                qWarning()<<__FUNCTION__<<": Fail to open file in overwritten mode";
                return false;
            }

            if(doc.isObject() && oldFileDoc.isObject()){
                QJsonObject oldFileConfigObj =oldFileDoc.object();
                QJsonObject configObj =doc.object();

                QJsonObject::iterator it;
                for(it=configObj.begin();it!=configObj.end();it++)
                {
                    oldFileConfigObj[it.key()] =it.value();
                }

                QJsonDocument writeDoc(oldFileConfigObj);
                QByteArray writeBytes = writeDoc.toJson();
                configFile.write(writeBytes);
                configFile.close();

                emit q_ptr->configFileUpdated(fullFilePath);
                return true;
            }else{
                QByteArray bytes = doc.toJson();
                configFile.write(bytes);
                configFile.close();

                emit q_ptr->configFileUpdated(fullFilePath);
                return true;
            }
        }
    }
}

QJsonValue QConfigerPrivate::readConfig(const QString& filePath)
{
    QUrl fileUrl(filePath);
    QString fullFilePath;

    if(fileUrl.isRelative()){
        fullFilePath = m_baseDir+"/"+filePath;
    }else{
        fullFilePath = filePath;
    }

    QFile configFile(fullFilePath);

    if(!configFile.exists())
    {
        qWarning()<<__FUNCTION__<<": File not exists";
        return QJsonValue(QJsonValue::Undefined);
    }
    else
    {
        if(!configFile.open(QFile::ReadOnly))
        {
            qWarning()<<__FUNCTION__<<": Fail to open file for read";
            return QJsonValue(QJsonValue::Undefined);
        }
        else
        {
            QByteArray bytes = configFile.readAll();
            configFile.close();

            QJsonParseError e;
            QJsonDocument doc = QJsonDocument::fromJson(bytes,&e);
            if(e.error==QJsonParseError::NoError)
            {
                return QJsonValue::fromVariant(doc.toVariant());
            }
            else
            {
                return QJsonValue(QJsonValue::Undefined);
            }
        }
    }
}

bool QConfigerPrivate::renameConfig(const QString&  filePath, const QString&  newFileName)
{
    QUrl fileUrl(filePath);
    QString fullFilePath;
    if(fileUrl.isRelative()){
        fullFilePath = m_baseDir+"/"+filePath;
    }else{
        fullFilePath = filePath;
    }

    QUrl fileUrl2(newFileName);
    QString fullFilePath2;
    if(fileUrl2.isRelative()){
        fullFilePath2 = m_baseDir+"/"+newFileName;
    }else{
        fullFilePath2 = newFileName;
    }

    QFile configFile(fullFilePath);

    if(configFile.rename(fullFilePath2)){

        emit q_ptr->configFileRenamed(fullFilePath,fullFilePath2);
        return true;
    }else{
        qWarning()<<__FUNCTION__<<":"<<configFile.errorString()<<fullFilePath2;
        return false;
    }
}

bool QConfigerPrivate::removeConfig(const QString&  filePath)
{
    QUrl fileUrl(filePath);
    QString fullFilePath;
    if(fileUrl.isRelative()){
        fullFilePath = m_baseDir+"/"+filePath;
    }else{
        fullFilePath = filePath;
    }

    QFile configFile(fullFilePath);

    if(configFile.remove()){

        emit q_ptr->configFileRemoved(fullFilePath);
        return true;
    }else{
        qWarning()<<__FUNCTION__<<":"<<configFile.errorString()<<fullFilePath;
        return false;
    }
}

